<?php
namespace Pinenut\Model;

class Finder {
	
	const OP_EQ = '==';
	const OP_IN = 'in';
	const OP_NEQ = '!=';
	const OP_NIN = 'nin';	
	const OP_GTE = 'gte';
	const OP_GT = 'gt';
	const OP_LTE = 'lte';
	const OP_LT = 'lt';
	
	/**
	 * @var \Pinenut\Model
	 */
	protected $class;
	
	
	public $where = array();
	
	public $fields = array();
	
	public $limit = 1000;
	public $offset = 0;
	
	protected $active = false;
	
	protected $_defaultField = ''; // поле в сложной структуре документа, в котором будут происходить все обновления
								// добавляется ко всем именам полей
								// так же используется в условии
	
	/**
	 * Установка модификатора определяющего, что модификация полей идёт внутри элемента множества, 
	 * предварительно выбранного в условии
	 * @var boolean
	 */
	protected $_inSet = false;
	
	function __construct($class) {
		$this->class = $class;
		//print_r($class); exit;
	}
	
	
	protected function andWhere($field, $op, $value) {
		$field = $this->prepareField($field);
		$this->where[] = array('op'=>$op, 'field'=>$field, 'value' => $value);
	}
	
	/**
	 * Добавляем условие с отрицанием
	 * @param string $field
	 * @param mixed $value
	 * @return \Pinenut\Model\Finder|\Pinenut\Model\Updater
	 */
	function whereNot($field, $value) {
		$op = static::OP_NEQ;
		if (is_array($value)) {
			$op = static::OP_NIN;
		}
		
		$this->andWhere($field, $op, $value);
		
		return $this;
	}
	
	/**
	 * Добавляем условие
	 * @param string $field
	 * @param mixed $value
	 * @param string $op
	 * @return \Pinenut\Model\Finder|\Pinenut\Model\Updater
	 */	
	function where($field, $value, $op = null) {
		if (null === $op) 
			$op = static::OP_EQ;
		
		if (is_array($value)) {
			$op = static::OP_IN;
		}
	
		$this->andWhere($field, $op, $value);
	
		return $this;
	}
	
	/**
	 * @param string $field
	 * @param mixed $value
	 * @return \Pinenut\Model\Finder|\Pinenut\Model\Updater
	 */
	function whereGreatOrEqual($field, $value) {
		$this->where($field, $value, static::OP_GTE);
		return $this;
	}
	
	/**
	 * @param boolean $enable
	 * @return \Pinenut\Model\Finder
	 */
	function active($enable = true) {
		$this->active = $enable;
		return $this;
	}
	
	/**
	 * @param int $value
	 * @return \Pinenut\Model\Finder
	 */
	function offset($value = 0) {
		$this->offset = $value;
		return $this;
	}
	
	/**
	 * @param int $value
	 * @return \Pinenut\Model\Finder
	 */
	function limit($value = 1000) {
		$this->limit = $value;
		return $this;
	}
	
	/**
	 * @param string[] $fields
	 * @return \Pinenut\Model\Finder
	 */
	function fields(array $fields = array()) {
		$this->fields = $fields;
		return $this;
	}
	
	/**
	 * @return NULL|unknown|\Pinenut\Model
	 */
	function one() {
		
		$class = $this->class;
		
		$this->limit = 1;
//		print_r($this->where);exit;
		$raw = $class::select($this->where, $this->fields, null, $this->offset, $this->limit);
		$raw = reset($raw);
		if (empty($raw)) return null;
		
		if ($this->active) {
			
			$object = new $class();
			/* @var \Pinenut\Model $object */
//			echo 'one';print_r($raw);
			$object->populate($raw, true);
//			print_r($object);
			return $object;
		} else {
			return $raw;
		}
	}
	
	function count() {
		$class = $this->class;
		return $class::count($this->where);
	}
	
	function all() {
	
		$class = $this->class;
		
		$raws = $class::select($this->where, $this->fields, null, $this->offset, $this->limit);
		
//		echo "<pre>all";print_r($raw);exit;
		if (empty($raws)) return null;
	
		if ($this->active) {
			$objects = array();
			foreach ($raws as $raw) {
				$object = new $class();
				/* @var \Pinenut\Model $object */
				$object->populate($raw, true);
				$objects[] = $object;
			}
			return $objects;
		} else {
			return $raws;
		}
	}
	
	/**
	 * Устанавливаем имя поля, в котором идут все модификации и выборки 
	 * Используется для работу с поддокументами
	 * 
	 * @param string $field
	 * @param bool $inSet
	 * @return \Pinenut\Model\Finder|\Pinenut\Model\Updater
	 */
	function setDefaultField($field, $inSet = false) {
		$this->_defaultField = $field;
		$this->_inSet = $inSet;
		return $this;
	}
	
	
	function prepareField($field, $useInSet = false) {
		return $this->_defaultField
				. ($field&&$this->_defaultField?'.':'') // точка в случае наливия обоих составляющих
				. ($useInSet&&$field&&$this->_inSet?'$.':'')
				. $field;

	}
}
